// Simple program illustrating a cone with some
// mad skillz shootin some hoopz.

#ifdef __APPLE_CC__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#include <math.h>

// The display list's ID for use in efficient drawing
static int displayListID;

// Angular component of the arc
static GLfloat theta = 1;
static const float PI = 3.141592;

// Define the FPS in the animation
static const int FPS = 60;

// Clears the window and depth buffer and draws the cone and torus from the
// display list... then draws the sphere based on the value of theta
void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glCallList(displayListID);

    // Draw the sphere in motion
    glPushMatrix();
        glTranslatef(-1.75 + theta * 1.25, -1.1 + 3 * sin(theta), -0.20);
        glRotatef(120.0, 1.0, 0.2, -0.25);
        glutSolidSphere(0.50, 50, 50);
    glPopMatrix();
    glFlush();
    glutSwapBuffers();
}

// We don't want the scene to get distorted when the window size changes, so
// we need a reshape callback.  We'll always maintain a range of -2.5..2.5 in
// the smaller of the width and height for our viewbox, and a range of -10..10
// for the viewbox depth.
void reshape(GLint w, GLint h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    GLfloat aspect = GLfloat(w) / GLfloat(h);
    glLoadIdentity();
    if (w <= h) {
        // width is smaller, so stretch out the height
        glOrtho(-2.5, 2.5, -2.5/aspect, 2.5/aspect, -10.0, 10.0);
    } else {
        // height is smaller, so stretch out the width
        glOrtho(-2.5*aspect, 2.5*aspect, -2.5, 2.5, -10.0, 10.0);
    }
}

// Timer to move the sphere!
void timer(int v) {
    theta += 0.1;
    if (theta > 2 * PI) {
        theta = 0;
    }
    glutPostRedisplay();
    glutTimerFunc(1000 / FPS, timer, 0);
}

// Performs application specific initialization, including some lighting
// and various material coloring.
void init() {
    // Define the lighting and colors
    GLfloat black[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat davidsColor[] = { 0.843, 0.27, 0.45, 1.0 }; // AKA "Rust"
    GLfloat andrewsColor[] = { 0.125, 0.129, 0.977, 1.0}; // AKA "Kinda Blueberry"
    GLfloat yellow[] = { 1.0, 1.0, 0.0, 1.0 };
    GLfloat cyan[] = { 0.0, 1.0, 1.0, 1.0 };
    GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat direction[] = { -1.0, 1.0, 1.0, 0.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, davidsColor);
    glMaterialfv(GL_FRONT, GL_SPECULAR, white);
    glMaterialf(GL_FRONT, GL_SHININESS, 30);

    glLightfv(GL_LIGHT0, GL_AMBIENT, black);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, yellow);
    glLightfv(GL_LIGHT0, GL_SPECULAR, white);
    glLightfv(GL_LIGHT0, GL_POSITION, direction);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    displayListID = glGenLists(1);
    glNewList(displayListID, GL_COMPILE);

    glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, davidsColor);

            // Rotate the scene so we can see the tops of the shapes.
            glRotatef(-20.0, 1.0, 0.0, 0.0);

            // Make a torus
            glPushMatrix();
                glTranslatef(1.75, 0.0, 0.0);
                glRotatef(90.0, 1.0, 0.0, 0.0);
                glutSolidTorus(0.175, 1.25, 16, 40);
            glPopMatrix();

            // Make a cone
            glPushMatrix();
                glTranslatef(-1.75, -0.5, 0.0);
                glRotatef(120.0, 1.0, 0.2, -0.25);
                glutSolidCone(1.0, 2.0, 70, 12);
            glPopMatrix();

        glPopMatrix();
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, andrewsColor);
    glEndList();

    glEnable(GL_DEPTH_TEST);
}

// The usual application statup code.
int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowPosition(80, 80);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Spheres and Cones!");
    glutTimerFunc(100, timer, 0);
    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    init();
    glutMainLoop();
}
